﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data.Entity;

using bdtheque.data.interfaces;
using bdtheque.data.poco;
using System.Data;

namespace bdtheque.data.dataaccess
{
    // Abstract base class used to be able to create generic Repository Dictionaries within the UnitOfWork implementations
    public abstract class Repository
    {
        protected DbContext Context { get; private set; }        

        public Repository(DbContext context)
        {
            if (context == null)
                throw new ArgumentNullException("DbContext");

            this.Context = context;
        }
    }

    // Implementation of the Repository Pattern using a generic approach
    public class Repository<T> : Repository, IRepository<T> 
        where T : class
    {                
        public Repository(DbContext context) : base(context){}

        public IEnumerable<T> GetAll()
        {
            return this.Context.Set<T>().AsEnumerable<T>();
        }

        public IEnumerable<T> GetAll(string includePath)
        {
            return this.Context.Set<T>().Include(includePath).AsEnumerable<T>();
        }

        public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
        {
            return this.Context.Set<T>().Where(predicate).AsEnumerable<T>();
        }

        public IEnumerable<T> Find(Expression<Func<T, bool>> predicate, string includePath)
        {
            return this.Context.Set<T>().Include(includePath).Where(predicate).AsEnumerable<T>();
        }

        public T SingleOrDefault(Expression<Func<T, bool>> predicate)
        {
            return this.Context.Set<T>().SingleOrDefault(predicate);
        }
        
        public T SingleOrDefault(Expression<Func<T, bool>> predicate, string includePath)
        {
            return this.Context.Set<T>().Include(includePath).SingleOrDefault(predicate);
        }

        public T FirstOrDefault(Expression<Func<T, bool>> predicate)
        {
            return this.Context.Set<T>().FirstOrDefault(predicate);
        }

        public T FirstOrDefault(Expression<Func<T, bool>> predicate, string includePath)
        {
            return this.Context.Set<T>().Include(includePath).FirstOrDefault(predicate);
        }

        public void AddEntity(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("Entity");

            this.Context.Set<T>().Add(entity); ;
        }

        public void RemoveEntity(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("Entity");

            this.Context.Set<T>().Remove(entity); ;
        }

        public void AttachEntity(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("Entity");

            this.Context.Set<T>().Attach(entity);
            this.Context.Entry(entity).State = EntityState.Modified;
        }

        public void DetachEntity(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("Entity");

            this.Context.Detach(entity);
        }
    }
}
